﻿using JESAI.DistributedLock.Internal;
using JESAI.DistributedLock.Internal.Data;
using System;
using System.Data;
using System.Threading;
using System.Threading.Tasks;

namespace JESAI.DistributedLock.Oracle;

/// <summary>
/// 使用DBMS_lock包为Oracle数据库实现可升级的分布式读写器锁。
/// </summary>
public sealed partial class OracleDistributedReaderWriterLock : IInternalDistributedUpgradeableReaderWriterLock<OracleDistributedReaderWriterLockHandle, OracleDistributedReaderWriterLockUpgradeableHandle>
{
    private readonly IDbDistributedLock _internalLock;

    /// <summary>
    /// 使用提供的构造新锁 <paramref name="name"/>. 
    /// 
    /// 提供的<paramref name="connectionString"/>将用于连接到数据库。
    /// 
    /// 除非指定了<paramref name="exactName"/>，否则将转义/散列<paramref name="name"/>以确保名称有效性。
    /// </summary>
    public OracleDistributedReaderWriterLock(string name, string connectionString, Action<OracleConnectionOptionsBuilder>? options = null, bool exactName = false)
        : this(name, exactName, n => OracleDistributedLock.CreateInternalLock(n, connectionString, options))
    {
    }

    /// <summary>
    /// 使用提供的构造新锁 <paramref name="name"/>.
    /// 
    /// 提供的<paramref name="connection"/>将用于连接到数据库，并提供锁作用域。假设它由外部管理，不会打开或关闭。
    /// 
    /// 除非指定了 <paramref name="exactName"/>，否则将转义/散列<paramref name="name"/>以确保名称有效性。
    /// </summary>
    public OracleDistributedReaderWriterLock(string name, IDbConnection connection, bool exactName = false)
        : this(name, exactName, n => OracleDistributedLock.CreateInternalLock(n, connection))
    {
    }

    private OracleDistributedReaderWriterLock(string name, bool exactName, Func<string, IDbDistributedLock> internalLockFactory)
    {
        this.Name = OracleDistributedLock.GetName(name, exactName);
        this._internalLock = internalLockFactory(this.Name);
    }

    /// <summary>
    /// 实现 <see cref="IDistributedLock.Name"/>
    /// </summary>
    public string Name { get; }

    async ValueTask<OracleDistributedReaderWriterLockUpgradeableHandle?> IInternalDistributedUpgradeableReaderWriterLock<OracleDistributedReaderWriterLockHandle, OracleDistributedReaderWriterLockUpgradeableHandle>.InternalTryAcquireUpgradeableReadLockAsync(
        TimeoutValue timeout,
        CancellationToken cancellationToken)
    {
        var innerHandle = await this._internalLock
            .TryAcquireAsync(timeout, OracleDbmsLock.UpdateLock, cancellationToken, contextHandle: null).ConfigureAwait(false);
        return innerHandle != null ? new OracleDistributedReaderWriterLockUpgradeableHandle(innerHandle, this._internalLock) : null;
    }

    async ValueTask<OracleDistributedReaderWriterLockHandle?> IInternalDistributedReaderWriterLock<OracleDistributedReaderWriterLockHandle>.InternalTryAcquireAsync(
        TimeoutValue timeout,
        CancellationToken cancellationToken,
        bool isWrite)
    {
        var innerHandle = await this._internalLock
            .TryAcquireAsync(timeout, isWrite ? OracleDbmsLock.ExclusiveLock : OracleDbmsLock.SharedLock, cancellationToken, contextHandle: null).ConfigureAwait(false);
        return innerHandle != null ? new OracleDistributedReaderWriterLockNonUpgradeableHandle(innerHandle) : null;
    }
}
